<?php
/**
 * Created by PhpStorm.
 * User: Knight
 * Date: 2019/1/30
 * Time: 17:10
 */

namespace app\api\service;


use app\api\exception\ParameterException;
use app\api\exception\StepException;
use app\api\exception\WxException;
use app\api\model\UserDrawConf;
use app\api\model\UserRegister;
use app\api\model\UserStep;
use app\api\model\UserStepRecord;
use app\api\util\wechat\WXBizDataCrypt;
use app\api\model\UserStep as UserStepModel;
use app\api\model\UserStepCity as UserStepCityModel;
use app\api\model\StepCityImg as UserStepCityImgModel;
use app\api\model\StepCity as StepCityModel;
use app\api\model\WxUser as WxUserModel;
use app\api\model\UserRegister as UserRegisterModel;
use app\api\model\UserDrawConf as UserDrawConfModel;
use app\api\model\UserStepRecord as UserStepRecordModel;
use app\api\model\UserRegisterRecord as UserRegisterRecordModel;
use think\Cache;
use think\Db;
use think\Log;

class Step
{
    /**
     * 获取步数
     */
    public function getRunData($params, $unionid)
    {
        $appid = config('APPID');
        $sessionKey = $params['sessionkey'];
        $encryptedData = $params['encrypteddata'];
        $iv = $params['iv'];
        $pc = new WXBizDataCrypt($appid, $sessionKey);
        $errCode = $pc->decryptData($encryptedData, $iv, $data);
        if ($errCode == 0) {//解密成功
            $data = json_decode($data, true);
            //当前最后一天的数据
            if (!array_key_exists('stepInfoList', $data)) {
                throw new WxException(['errCode' => 20001, 'msg' => '用户未授权跑步数据']);
            }
            $lastRunData = array_pop($data['stepInfoList']);
            $step = $this->getUserStep($unionid, $lastRunData);//计算用户步数

            return $step;

        } else {//解密失败
            throw new WxException(['msg' => '加密参数错误']);
        }

    }

    /**
     * 计算用户新增步数并返回
     * @param $unionid
     * @param $lastRunData 微信返回的今日步数
     * @return mixed
     */
    protected function getUserStep($unionid, $lastRunData)
    {
        $userStepModel = new UserStepModel();
        $userRun = $userStepModel->where('unionid', $unionid)->find();//数据库用户步数记录

        if ($lastRunData['timestamp'] < strtotime(date("Y-m-d"))) {//微信步数已过期
            return ['step' => 0, 'max' => 0];
        }
        if (empty($userRun)) {//1.无用户步数记录
            $step = $lastRunData['step'] >= intval(config('step.longStep')) ? intval(config('step.longStep')) : $lastRunData['step'];//返回步数(限制每日最高领取步数);
            return ['step' => $step, 'max' => 0];
        }
        //2.有用户步数记录
        if ($userRun['day_time'] < $lastRunData['timestamp']) {//2.1当前用户记录是否已过期(当前数据库步数已不是本日数据)

            if($lastRunData['step'] >= intval(config('step.longStep'))){
                //当前步数已超过每日最高上限
                $step = intval(config('step.longStep'));
                $max=1;//当到达日上限时返回1;未到达返回0
            }else{
                //当前步数未超过每日最高上限
                $step = $lastRunData['step'];
                $max=0;
            }

            return ['step' => $step, 'max' => $max];

        } else {//2.2当前用户记录未过期(当前步数是本日数据)
            if ($lastRunData['step'] >= intval(config('step.longStep'))) {
                //当前步数已超过每日最高上限
                $step = intval(config('step.longStep')) - $userRun['day_step'];
                $max = 1;//当到达日上限时返回1;未到达返回0
            } else {
                //当前步数未超过每日最高上限
                $step = $lastRunData['step'] - $userRun['day_step'];
                $max = 0;
            }
            return ['step' => $step, 'max' => $max];
        }


    }


    /**
     * 初始化用户数据
     */
    public function initData($unionid, $wxuser)
    {

        //初始化步数
        $userStepModel = new UserStepModel();
        $userStepModel->unionid = $unionid;
        $userStepModel->nickname = $wxuser['nickName'];
        $userStepModel->headImgurl = $wxuser['avatarUrl'];
        $userStepModel->step = 0;
        $userStepModel->day_step = 0;
        $userStepModel->day_time = strtotime(date('Y-m-d'));
        $userStepModel->created_time = date('Y-m-d H:i:s');
        $userStepModel->together('wxUser,userStepCity,userRegister,userDrawConf');
        //初始化微信用户数据
        $wxUser = new WxUserModel();
        $wxUser->openid = $wxuser['openid'];
        $wxUser->unionid = $wxuser['unionid'];
        $wxUser->nickName = $wxuser['nickName'];
        $wxUser->avatarUrl = $wxuser['avatarUrl'];
        $wxUser->gender = $wxuser['gender'];
        $wxUser->created_time = $wxuser['created_time'];
        $userStepModel->wxUser = $wxUser;

        //初始化用户城市数据
        $userStepCity = new UserStepCityModel();
        $userStepCity->unionid = $unionid;
        $userStepCity->city_a = 'N';
        $userStepCity->city_b = 'N';
        $userStepCity->city_c = 'N';
        $userStepCity->city_d = 'N';
        $userStepCity->city_e = 'N';
        $userStepCity->city_f = 'N';
        $userStepCity->city_g = 'N';
        $userStepCity->city_h = 'N';
        $userStepCity->city_i = 'N';
        $userStepCity->city_j = 'N';
        $userStepCity->created_time = date('Y-m-d H:i:s');
        $userStepModel->userStepCity = $userStepCity;
        //初始化用户当日签到数据
        $userRegister = new UserRegisterModel();
        $userRegister->unionid = $unionid;
        $userRegister->day_one = 'N';
        $userRegister->day_two = 'N';
        $userRegister->day_three = 'N';
        $userRegister->last_time = 0;
        $userStepModel->userRegister = $userRegister;
        //初始化用户可抽奖次数
        $userDrawConf = new UserDrawConfModel();
        $userDrawConf->unionid = $unionid;
        $userDrawConf->num = 0;
        $userDrawConf->created_time = date('Y-m-d H:i:s');
        $userStepModel->userDrawConf = $userDrawConf;
        $res = $userStepModel->save();
        if (empty($res)) {
            throw new WxException(['msg' => '用户初始化参数错误']);
        }
    }

    /**
     * 获取用户点亮城市数据
     * @return 返回用户点亮城市数据
     */
    public function getCityLight($unionid)
    {
        $userStepCityModel = new UserStepCityModel();
        $citys = $userStepCityModel->where('unionid', $unionid)
            ->field('city_a,city_b,city_c,city_d,city_e,city_f,city_g,city_h,city_i,city_j')
            ->find();
        if (empty($citys)) {
            $userStepCityModel->insert([
                'unionid' => $unionid,
                'city_a' => 'N',
                'city_b' => 'N',
                'city_c' => 'N',
                'city_d' => 'N',
                'city_e' => 'N',
                'city_f' => 'N',
                'city_g' => 'N',
                'city_h' => 'N',
                'city_i' => 'N',
                'city_j' => 'N',
                'created_time' => date('Y-m-d H:i:s')
            ]);
            $citys = ['city_a' => 'N', 'city_b' => 'N', 'city_c' => 'N', 'city_d' => 'N', 'city_e' => 'N', 'city_f' => 'N', 'city_g' => 'N', 'city_h' => 'N', 'city_i' => 'N', 'city_j' => 'N'];
        } else {
            $citys = $citys->toArray();
        }

        $stepCityModel = new StepCityModel();
        $citysConifg = $stepCityModel->field('city_a,city_b,city_c,city_d,city_e,city_f,city_g,city_h,city_i,city_j')->find();//返回点亮city配置
        $userStepCityImgModel = new UserStepCityImgModel();
        $citysImgConfig = $userStepCityImgModel
            ->field('city_a,city_b,city_c,city_d,city_e,city_f,city_g,city_h,city_i,city_j')
            ->find();//返回点亮cityImg配置
        $userCityArr = [];
        foreach ($citys as $k => $v) {
            $userCityArr[$k] = ['states' => $v, 'step' => $citysConifg[$k], 'img' => $citysImgConfig[$k]];
        }
        return ['code' => 1, 'citys' => $userCityArr];

    }


    /**
     * 获取用户点亮城市数据,用户总步数,用户当日签到情况,用户可抽奖次数及新增步数
     * @param $unionid
     * @param $params
     * @return array
     */
    public function getCLAndSNAndRAndUDNAndStep($unionid,$params){
        $addstep=$this->getRunData($params,$unionid);//有效步数
        $arr=$this->getCLAndSNAndRAndUDN($unionid);
        $arr['stepmsg']=$addstep;
        return $arr;

    }


    /**
     * 获取用户当日签到情况
     * @param $unionid
     * @return 返回用户当前的签到情况
     */
    public function register($unionid)
    {
        $userRegisterModel=new UserRegisterModel();
        $userRegister=$userRegisterModel->where('unionid',$unionid)
            ->field('day_one,day_two,day_three,last_time')
            ->find();

        if(empty($userRegister)){
            //用户签到记录不存在
            $userRegisterModel->insert([
                'unionid'=>$unionid,
                'day_one'=>'N',
                'day_two'=>'N',
                'day_three'=>'N',
                'last_time'=>0
            ]);
            return ['reg_day'=>['day_one'=>'N','day_two'=>'N','day_three'=>'N'],'reg_state'=>'Y'];
        }else{
            $register=['day_one'=>$userRegister['day_one'],'day_two'=>$userRegister['day_two'],'day_three'=>$userRegister['day_three']];
            if($userRegister['last_time']==date('Ymd')){
                //本日已签到
                return ['reg_day'=>$register,'reg_state'=>'N'];
            }else{
                //本人未签到
                return ['reg_day'=>$register,'reg_state'=>'Y'];
            }

        }

    }


    /**
     * 获取用户点亮城市数据,用户总步数,用户当日签到情况,用户可抽奖次数
     */
    public function getCLAndSNAndRAndUDN($unionid)
    {

        $data = Db::table('zd_user_step')->alias('a')
            ->join('zd_step_city_user b', 'a.unionid=b.unionid')
            ->join('zd_user_register c', 'a.unionid=c.unionid')
            ->join('zd_user_draw_conf d', 'a.unionid=d.unionid')
            ->where('a.unionid', $unionid)
            ->find();
        $citys = ['city_a' => $data['city_a'],
            'city_b' => $data['city_b'],
            'city_c' => $data['city_c'],
            'city_d' => $data['city_d'],
            'city_e' => $data['city_e'],
            'city_f' => $data['city_f'],
            'city_g' => $data['city_g'],
            'city_h' => $data['city_h'],
            'city_i' => $data['city_i'],
            'city_j' => $data['city_j'],
        ];
        //1.重组用户城市点亮数据
        $citysConifg = Cache::get('citysConifg');
        if (empty($citysConifg)) {
            $stepCityModel = new StepCityModel();
            $citysConifg = $stepCityModel->field('city_a,city_b,city_c,city_d,city_e,city_f,city_g,city_h,city_i,city_j')->cache('citysConifg', 3600)->find();//返回点亮city配置并设置缓存1小时
        }
        $userCityArr = [];
        $advance="";
        $city=config('city');
        $index=-1;
        foreach ($citys as $k => $v) {
            if($v=='Y'){
                $index++;
            }
            $userCityArr[$k] = ['states' => $v, 'step' => $citysConifg[$k]];
        }

//        if($index==-1){
//            //无点亮城市
//            $advance=['无',$city[$index+1],$city[$index+2]];
//        }else if($index==8){
//            //点亮第九个城市
//            $advance=[$city[$index],$city[$index+1],'无'];
//
//        }else if($index==9){
//            //点亮最后第十个城市
//            $advance=[$city[$index],'无','无'];
//        }else{
//            //点亮第一个至第八个之间
//            $advance=[$city[$index],$city[$index+1],$city[$index+2]];
//        }

        if($index==-1){
            //无点亮城市
            $advance=['无','起点',$city[$index+1]];
        }else if($index==0){
            //点亮第一个城市
            $advance=['起点',$city[$index],$city[$index+1]];
        }else if($index==9){
            //点亮最后第十个城市
            $advance=[$city[$index-1],$city[$index],'无'];
        }else{
            //点亮第二个至第九个之间
            $advance=[$city[$index-1],$city[$index],$city[$index+1]];
        }

        //2.返回用户总步数
        $step = $data['step'];

        //3.返回用户签到情况
        $register = ['day_one' => $data['day_one'], 'day_two' => $data['day_two'], 'day_three' => $data['day_three']];

        //昨天的日期
        $lastTime = date('Ymd', strtotime(date("Y-m-d", strtotime("-1 day"))));
        if(date('Ymd')==$data['last_time']){
            //今日已签到,无需处理
        }else{
            //今日还未签到
            if($data['last_time']!=$lastTime){//昨日是否签到
                //昨日未签到
                $register = ['day_one' => 'N', 'day_two' =>  'N', 'day_three' =>  'N'];
            }else{
                //昨日已签到
                if($data['day_three']=='Y'){
                    //已完成3日连续签到
                    $register = ['day_one' => 'N', 'day_two' =>  'N', 'day_three' =>  'N'];
                }
            }
        }


        //本日的签到状态
        if ($data['last_time'] == date('Ymd')) {
            //本日已签到
            $reg = ['reg_day' => $register, 'reg_state' => 'N'];
        } else {
            //本人未签到
            $reg = ['reg_day' => $register, 'reg_state' => 'Y'];
        }

        //4.返回用户可抽奖次数
        $drawNum = $data['num'];
        return [
            'code' => 1,
            'citys' => $userCityArr,
            'stepNumber' => $step,
            'register' => $reg,
            'drawNum' => $drawNum,
            'advance'=>$advance
        ];
    }




    /**
     * 用户提交累计步数
     * @param $step
     */
    public function exchangeStep($step, $unionid)
    {

        $userStepModel = new UserStepModel();
        $userStepModel->startTrans();
        $userStepCityModel = new UserStepCityModel();
        $userStepCityModel->startTrans();
        $userDrawConfModel = new UserDrawConfModel();
        $userDrawConfModel->startTrans();

        try {
            $userStep = $userStepModel->where('unionid', $unionid)->find();
            //1.计入总步数和每日步数
            $nowTime = strtotime(date('Y-m-d'));
            $addStep = 0;//有效步数
            $userStep = $this->userStepAdd($userStep, $nowTime, $userStepModel, $step, $addStep);
            $stepNowNumber = $userStep->step;//当前用户的总步数
            $res1 = $userStep->save();


            //2.计算点亮城市
            $stepCityModel = new StepCityModel();
            $stepCity = $stepCityModel->find();//获取全局点亮城市配置
            $userStepCity = $userStepCityModel->where('unionid', $unionid)->find();//获取个人点亮城市数据
            $drawAdd = 0;//新增可抽奖次数
            $userStepCity = $this->userCityUpdate($stepCity->toArray(), $stepNowNumber, $userStepCity, $drawAdd);
            $res2 = $userStepCity->save();


            //3.计算抽奖次数
            $userDrawConf = $userDrawConfModel->where('unionid', $unionid)->find();//用户当前抽奖机会
            $userDrawConf->num += $drawAdd;
            $userDrawConf->updated_time = date('Y-m-d H:i:s');
            $res3 = $userDrawConf->save();

            if ($res1 && $res2 && $res3) {
                $userStepModel->commit();
                $userStepCityModel->commit();
                $userDrawConfModel->commit();

                $this->saveStepRecord($addStep, $unionid);//记录用户提交步数日志
            } else {
                $userStepModel->rollback();
                $userStepCityModel->rollback();
                $userDrawConfModel->rollback();
                throw new StepException();
            }
        } catch (StepException $e) {
            throw new StepException(['errorCode' => 50002, 'msg' => '步数提交失败,请重试']);
        }


    }

    /**
     * 计入总步数和每日步数
     * @param $userStep      数据库中用户步数数据
     * @param $nowTime       当前年月日时间戳
     * @param $userStepModel 用户步数model
     * @param $step          用户新增步数
     * @param $addStep       返回新增的有效步数
     * @return $userStep     数据库修改完的用户步数数据
     * @throws ParameterException
     */
    private function userStepAdd($userStep, $nowTime, $userStepModel, $step, &$addStep)
    {
        if (($userStep->day_time) == $nowTime) {
            //当天已提交过步数
            if (config('step.longStep') == intval($userStep['day_step'])) {
                //提交前步数已达到上限
                $userStepModel->rollback();
                throw new ParameterException(['errorCode' => 50001, 'msg' => '今日步数已达到上限']);
            } else if (intval(config('step.longStep')) <= intval($step) + $userStep['day_step']) {
                //提交后步数到达上限
                $addStep = intval(config('step.longStep')) - ($userStep->day_step);//新增有效步数
                $userStep->day_step = intval(config('step.longStep'));
            } else {
                //提交后未到达每日上限
                $addStep = intval($step);//新增有效步数
                $userStep->day_step = ($userStep->day_step) + $addStep;
            }
        } else {
            //当天未提交过步数
            if (intval(config('step.longStep')) <= intval($step)) {
                //提交后步数到达上限
                $addStep = intval(config('step.longStep'));//新增有效步数
                $userStep->day_step = $addStep;
            } else {
                //提交后未到达每日上限
                $addStep = intval($step);//新增有效步数
                $userStep->day_step = $addStep;
            }
        }
        $userStep->step = ($userStep->step) + $addStep;
        $userStep->day_time = $nowTime;
        $userStep->updated_time = date('Y-m-d H:i:s');
        return $userStep;
    }

    /**
     * 更新城市点亮情况,并且返回用户可抽奖次数
     * @param $stepCity 城市配置
     * @param $stepNowNumber 用户步数
     * @param $userStepCity 用户城市点亮数据
     * @param $drawAdd 抽奖次数
     * @return mixed
     */
    private function userCityUpdate($stepCity, $stepNowNumber, $userStepCity, &$drawAdd)
    {
        foreach ($stepCity as $k => $v) {
            if ($stepCity[$k] <= $stepNowNumber) {

                //当前城市可以点亮
                if ($userStepCity[$k] == 'N') {
                    //当前用户城市还未点亮,则可以点亮
                    $userStepCity[$k] = 'Y';
                    $drawAdd++;
                } else {
                    //当前用户城市已点亮
                    continue;
                }
            } else {
                break;
            }
        }
        $userStepCity->updated_time = date('Y-m-d H:i:s');
        return $userStepCity;
    }

    /**
     * 记录用户提交步数日志
     * @param $addStep 有效步数
     */
    private function saveStepRecord($addStep, $unionid)
    {
        $userStepRecordModel = new UserStepRecordModel();
        $userStepRecordModel->insert([
            'unionid' => $unionid,
            'step' => $addStep,
            'created_time' => date('Y-m-d H:i:s')
        ]);
    }


    /**
     * 返回排行榜数据
     */
    public function rankList($unionid, $page = 1, $limit = 100)
    {
        $userStepModel = new UserStepModel();
        $res = $userStepModel->field('from_base64(nickname) nickname,headImgurl,step')
            ->order('step', 'desc')
            ->limit($limit)->page($page)->select();
        if (empty($res)) {
            throw new StepException(['errorCode' => 50003, 'msg' => '排行榜没有更多数据了']);
        }
        $userStepRanking = $this->getUserRanking($unionid);

        return ['code' => 1, 'data' => $res, 'userRank' => $userStepRanking];
    }

    /**
     * 个人在排行榜中的排名数据
     * @param $unionid
     * @return mixed
     */
    private function getUserRanking($unionid)
    {
        $userStepModel = new UserStepModel();
        $userStepRanking = $userStepModel->query('select a.No,from_base64(a.nickname) nickname,a.headImgUrl,a.step from (
select (@i :=@i + 1) AS No,unionid,nickname,headImgUrl,step 
from zd_user_step,(SELECT @i := 0) AS it 
order by step desc
) a where a.unionid=?', [$unionid]);
        return $userStepRanking;
    }


    /**
     * 返回用户历史提交步数
     */
    public function userStepRecord($unionid)
    {
        $userStepRecordModel = new UserStepRecordModel();
        $res = $userStepRecordModel
            ->field('step,created_time')
            ->where('unionid', $unionid)
            ->order('created_time', 'desc')->select();
        return ['code' => 1, 'data' => $res];
    }

    /**
     * 用户签到
     * @param $unionid
     */
    public function userRegister($unionid)
    {
        $nowTime = date('Ymd');//当日时间戳
        $userRegisterModel = new UserRegisterModel();
        $userRegisterModel->startTrans();
        $userRegister = $userRegisterModel->where('unionid', $unionid)->find();
        if ($userRegister['last_time'] == $nowTime) {
            //当日已签到过
            throw new StepException(['errorCode' => 50004, 'msg' => '当日已签到过']);
        }

        $lastTime = date('Ymd', strtotime(date("Y-m-d", strtotime("-1 day"))));
        $drawState = false;//抽奖机会
        if ($userRegister['last_time'] == $lastTime) {
            //昨天已签到
            if ($userRegister['day_one'] == 'N') {
                //第一天签到
                $userRegister['day_one'] = 'Y';
            } else if ($userRegister['day_two'] == 'N') {
                //已连续签到1天
                $userRegister['day_two'] = 'Y';
            } else if ($userRegister['day_three'] == 'N') {
                //已连续签到2天
                $userRegister['day_three'] = 'Y';
                $drawState = true;
            } else {
                //3天已全部签到满
                $userRegister['day_two'] = 'N';
                $userRegister['day_three'] = 'N';
            }
        } else {
            //昨天未签到
            $userRegister['day_one'] = 'Y';
            $userRegister['day_two'] = 'N';
            $userRegister['day_three'] = 'N';
        }
        $userRegister['last_time'] = $nowTime;
        $res1 = $userRegister->save();

        $userDrawConfModel = new UserDrawConfModel();
        $userDrawConf=$userDrawConfModel->where('unionid', $unionid)->find();
        $userDrawConf->startTrans();

        //2.计算抽奖次数
        $draw_num=empty($userDrawConf['num'])?0:$userDrawConf['num'];//可抽奖次数
        if ($drawState) {
            $res2 = $userDrawConf
                ->save([
                    'num'=>$userDrawConf['num']+1,
                    'updated_time' => date('Y-m-d H:i:s')
                ]);
            $draw_num=$userDrawConf['num'];
        } else {
            $res2 = true;
        }

        //3.记录签到日志
        $userRegisterRecordModel = new UserRegisterRecordModel();
        $userRegisterRecordModel->startTrans();
        $userRegisterRecordModel->unionid = $unionid;
        $userRegisterRecordModel->register_time = date('Y-m-d H:i:s');
        $res3 = $userRegisterRecordModel->save();

        if ($res1 && $res2 && $res3) {
            $userRegisterModel->commit();
            $userDrawConf->commit();
            $userRegisterRecordModel->commit();
            //返回用户签到后数据
            $register=['day_one'=>$userRegister['day_one'],'day_two'=>$userRegister['day_two'],'day_three'=>$userRegister['day_three']];
            return ['code' => 1,'data'=>['reg_day'=>$register,'reg_state'=>'N','drawNum'=>$draw_num]];

        } else {
            $userRegisterModel->rollback();
            $userDrawConf->rollback();
            $userRegisterRecordModel->rollback();
            throw new StepException(['errorCode' => 50005, 'msg' => '签到错误']);
        }

    }


}